package com.ajavaer.framework.weixin;

import com.ajavaer.framework.AjavaerCache;
import com.ajavaer.framework.cache.type.CacheItem;
import com.ajavaer.framework.common.message.Message;
import com.ajavaer.framework.common.tools.Base64Tools;
import com.ajavaer.framework.common.tools.HttpTools;
import com.ajavaer.framework.common.tools.JsonTools;
import com.ajavaer.framework.config.WeixinConfig;
import com.ajavaer.framework.config.WeixinMiniProgramConfig;
import com.ajavaer.framework.exception.WeixinException;
import com.ajavaer.framework.handle.CacheHandle;
import com.ajavaer.framework.message.WeixinMinProgramSession;
import com.ajavaer.framework.message.WeixinMiniProgramAccessToken;
import com.ajavaer.framework.tools.WeixinAES;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * 微信小程序
 */
@Component
public class WeixinMiniProgram implements InitializingBean {
    private Logger log = LoggerFactory.getLogger(WeixinMiniProgram.class);
    @Resource
    private WeixinMiniProgramConfig weixinMiniProgramConfig;
    @Resource
    private WeixinConfig weixinConfig;
    private CacheHandle<String, String> cacheHandle;


    /**
     * 获得accessToken,如果不存在,则从微信服务器获取一个,并缓存起来,缓存有效期7200秒,如果获取失败,则返回错误信息
     *
     * @return
     */
    public Message<String> getAccessToken() {
        Message<String> result = Message.success("OK");
        String accessToken = cacheHandle.get(weixinMiniProgramConfig.getAccessTokenCacheKey(), key -> {
            String url = weixinMiniProgramConfig.getGet_token_url();
            Message<WeixinMiniProgramAccessToken> accessTokenResult = HttpTools.get(url, WeixinMiniProgramAccessToken.class);
            log.debug("Get Access Token From Wx,url:{},res:{}", url, JsonTools.beanToJson(accessTokenResult));
            if (accessTokenResult.isSuccess()) {
                WeixinMiniProgramAccessToken resultData = accessTokenResult.getData();
                if (resultData.success()) {
                    return CacheItem.of(key, resultData.getAccess_token(), resultData.getExpires_in(), TimeUnit.SECONDS);
                } else {
                    result.setMsg("get AccessToken error:" + resultData.getErrmsg());
                    result.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
                    log.error("weixin error:{}", JsonTools.beanToJson(resultData));
                }
            } else {
                result.setMsg(accessTokenResult.getMsg());
                result.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
                log.error("connect weixin server error:{}", accessTokenResult.getMsg());
            }
            return null;
        });
        result.setData(accessToken);
        return result;
    }

    /**
     * <pre>
     * //正常返回的JSON数据包
     * {
     * "openid": "OPENID",
     * "session_key": "SESSIONKEY",
     * }
     *
     * //满足UnionID返回条件时，返回的JSON数据包
     * {
     * "openid": "OPENID",
     * "session_key": "SESSIONKEY",
     * "unionid": "UNIONID"
     * }
     * //错误时返回JSON数据包(示例为Code无效)
     * {
     * "errcode": 40029,
     * "errmsg": "invalid code"
     * }
     * </pre>
     *
     * @param code 登录code
     * @return
     * @see <a href='https://developers.weixin.qq.com/miniprogram/dev/api/code2Session.html'>官方文档</a>
     */
    public Message<WeixinMinProgramSession> jsCode2Session(String code) {
        Message<WeixinMinProgramSession> weixinMinProgramSessionMessage = HttpTools.get(weixinMiniProgramConfig.getCode2session_url(code), WeixinMinProgramSession.class);
        //存入缓存
        if (weixinMinProgramSessionMessage.isSuccess() && weixinMinProgramSessionMessage.getData().success()) {
            String openid = weixinMinProgramSessionMessage.getData().getOpenid();
            String sessionCacheKey = weixinMiniProgramConfig.getSessionCacheKey(openid);
            cacheHandle.put(sessionCacheKey, weixinMinProgramSessionMessage.getData().getSession_key());
            weixinMinProgramSessionMessage.getData().setSession_key("");
        }
        return weixinMinProgramSessionMessage;
    }

    /**
     * 通过openId从缓存中获取sessionKey
     *
     * @param openId
     * @return
     */
    public String findSessionKey(String openId) {
        return cacheHandle.getIfPresent(weixinMiniProgramConfig.getSessionCacheKey(openId));
    }

    /**
     * 解密用户敏感数据获取用户信息
     *
     * @param sessionKey    数据进行加密签名的密钥
     * @param iv            加密算法的初始向量
     * @param encryptedData 包括敏感数据在内的完整用户信息的加密数据
     */
    public String decodeUserInfo(String sessionKey, String iv, String encryptedData) throws Exception {
        // 被加密的数据
        byte[] dataByte = Base64Tools.decode(encryptedData);
        // 加密秘钥
        byte[] keyByte = Base64Tools.decode(sessionKey);
        // 偏移量
        byte[] ivByte = Base64Tools.decode(iv);
        byte[] resultByte = WeixinAES.decrypt(dataByte, keyByte, ivByte);
        if (null != resultByte && resultByte.length > 0) {
            return new String(resultByte, "UTF-8");
        }

        return null;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.cacheHandle = AjavaerCache.get(weixinConfig.getCacheEngine());
    }
}
